{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# PYNQ tutorial: Xlnk and Contiguous Memory Allocation\n",
    "\n",
    "`Xlnk()` can assign a contiguous block of memory. The data in the memory buffer can be transferred efficiently between the PS and the PL. \n",
    "\n",
    "Note: The `Xlnk()` driver is overlay-independent, meaning it can be used no matter what overlay you are using.\n",
    "\n",
    "## Preparation\n",
    "\n",
    "In the following cell we will define a couple of useful functions."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from time import sleep\n",
    "from pprint import pprint\n",
    "\n",
    "def get_kb(mmu):\n",
    "    return int(mmu.cma_stats()['CMA Memory Available']//1024)\n",
    "\n",
    "def get_bufcount(mmu):\n",
    "    return int(memmanager.cma_stats()['Buffer Count'])\n",
    "\n",
    "def print_kb(mmu):\n",
    "    print(\"Available Memory (KB): \" + str(get_kb(mmu)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "## Step 1: Create an instance of Xlnk\n",
    "Xlnk instance will be labeled `mmu` (Memory Management Unit) throughout this notebook."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "from pynq import Xlnk\n",
    "xlnk = Xlnk()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 2: Check the status"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pprint(xlnk.cma_stats())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 3: Allocate the memory"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np \n",
    "print(\"Before memory allocation:\")\n",
    "print_kb(xlnk)\n",
    "\n",
    "buffer = xlnk.cma_array(shape=(10000000,), dtype=np.float32)\n",
    "\n",
    "print(\"After memory allocation:\")\n",
    "print_kb(xlnk)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 4: Check the memory buffer address\n",
    "\n",
    "The virtual address can be used by Linux. The Physical address can be passed to a peripheral in the PL."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(\"Buffer pointer address (physical memory):\")\n",
    "print(hex(buffer.physical_address))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 5: Free the memory\n",
    "It is always a good practice to free the contiguous memory after use. This prevents memory leaks from the program."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "del buffer\n",
    "print(xlnk.cma_stats())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print_kb(xlnk)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note, it is normal that the available memory may not be exactly the same as the previous number. "
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
